package dao.impl;

import com.google.common.collect.ImmutableMap;
import dao.GenericDao;
import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;

import javax.annotation.PostConstruct;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by aceyin on 2015/6/1 0001.
 */
abstract class BaseGenericDao<ENTITY, ID extends Serializable> implements GenericDao<ENTITY, ID> {
    private static final String ID_INSERT = "insert";
    private static final String ID_UPDATE = "update";
    private static final String ID_UPDATE_PARAM = "updateParam";
    private static final String ID_DELETE = "delete";
    private static final String ID_DELETE_PARAM = "deleteParam";
    private static final String ID_TRUNCATE = "truncate";
    private static final String ID_SELECT = "select";
    private static final String ID_SELECT_PK = "selectPk";
    private static final String ID_SELECT_PARAM = "selectParam";
    private static final String ID_SELECT_FK = "selectFk";
    private static final String ID_COUNT = "count";
    private static final String ID_COUNT_PARAM = "countParam";
    private static final String ID_BATCH_INSERT = "batchInsert";

    @Autowired
    private SqlSession sqlSession;
    private String namespace;

    @PostConstruct
    public void init() {
        Class<? extends BaseGenericDao> clazz = getClass();
        Type type = clazz.getGenericSuperclass();
        if (type instanceof ParameterizedType) {
            Type[] types = ((ParameterizedType) type).getActualTypeArguments();
            namespace = types[0].getTypeName();
        }
        Assert.notNull(namespace, "Entity generic type should be declared for DAO class '" + clazz + "'");
    }

    protected String getNameSpace() {
        return namespace;
    }

    protected SqlSession getSqlSession() {
        return sqlSession;
    }

    protected int getOffset(int page, int rows) {
        int i = (page - 1);
        int offset = (i < 0 ? 0 : i) * rows;
        return offset;
    }

    /**
     * Select one object from database according to the given parameters.
     *
     * @param sqlId    - the sqlid in mybatis mapping xml file.
     * @param params   - query parameter
     * @param <ENTITY>
     * @return
     */
    protected <ENTITY> ENTITY queryOne(String sqlId, Map<String, Object> params) {
        String s = getSql(sqlId);
        if (params == null) {
            return sqlSession.selectOne(s);
        }
        return sqlSession.selectOne(s, params);
    }

    /**
     * Select list from database according to the given parameters.
     *
     * @param sqlId    - the sqlid in mybatis mapping xml file.
     * @param params   -  query parameter
     * @param <ENTITY>
     * @return
     */
    protected <ENTITY> List<ENTITY> queryList(String sqlId, Map<String, Object> params) {
        String s = getSql(sqlId);
        if (params == null) {
            return sqlSession.selectList(s);
        }
        return sqlSession.selectList(s, params);
    }

    /**
     * Update by given params.
     *
     * @param sqlId
     * @param params
     */
    protected void update(String sqlId, Map<String, Object> params) {
        sqlSession.update(getSql(sqlId), params);
    }

    protected ImmutableMap.Builder<String, Object> newMapBuilder() {
        return ImmutableMap.<String, Object>builder();
    }

    @Override
    public void batchDelete(List<ID> list) {

    }

    @Override
    public void batchUpdate(List<ENTITY> list) {

    }

    @Override
    public void batchInsert(List<ENTITY> list) {
        sqlSession.insert(getNameSpace() + "." + ID_BATCH_INSERT, list);
    }

    @Override
    public List<ENTITY> list(Map<String, Object> param) {
        return sqlSession.selectList(getNameSpace() + "." + ID_SELECT_PARAM, param);
    }

    @Override
    public List<ENTITY> list(int page, int rownum) {
        Map<String, Integer> params = new HashMap<>();
        params.put("start", page);//TODO: page * pageSize
        params.put("limit", rownum);
        return sqlSession.selectList(getNameSpace() + "." + ID_SELECT, params);
    }

    @Override
    public ENTITY get(ID id) {
        return sqlSession.selectOne(getNameSpace() + "." + ID_SELECT, id);
    }

    @Override
    public int count(Map<String, Object> param) {
        return sqlSession.selectOne(getNameSpace() + "." + ID_COUNT_PARAM, param);
    }

    @Override
    public int count() {
        return sqlSession.selectOne(getNameSpace() + "." + ID_COUNT);
    }

    @Override
    public int count(String sqlId, Map<String, Object> params) {
        return sqlSession.selectOne(sqlId, params);
    }

    @Override
    public int delete(Map<String, Object> param) {
        return sqlSession.delete(getNameSpace() + "." + ID_DELETE_PARAM, param);
    }

    @Override
    public int delete(ID id) {
        return sqlSession.delete(getNameSpace() + "." + ID_DELETE, id);
    }

    @Override
    public int update(ENTITY entity) {
        return sqlSession.update(getNameSpace() + "." + ID_UPDATE, entity);
    }

    @Override
    public void insert(ENTITY entity) {
        sqlSession.insert(getNameSpace() + "." + ID_INSERT, entity);
    }

    private String getSql(String sqlId) {
        return getNameSpace() + "." + sqlId;
    }
}
